<?php

namespace App\Kuafu\WaiMai\Service;

use App\Kuafu\Mqtt\Enum\MqttCodeEnum;
use App\Kuafu\Mqtt\Service\MqttService;
use App\Kuafu\Nsq\Enum\NsqCodeEnum;
use App\Kuafu\Nsq\Service\NsqService;
use App\Kuafu\Order\Enum\CodeEnum;
use App\Kuafu\Shop\Service\DouyinLibrary\DouyinWaiMaiService;
use App\Kuafu\Shop\Service\ShopService;
use App\Kuafu\WaiMai\Channel\BaseWaiMai;
use App\Kuafu\WaiMai\Enum\DyWaiMaiEnum;
use App\Kuafu\WaiMai\Enum\WaiMaiDBEnum;
use App\Kuafu\WaiMai\Exceptions\WaiMaiException;
use App\Kuafu\WaiMai\Model\WaiMaiOrderIdShopIdMappingModel;
use App\Kuafu\WaiMai\Model\WaiMaiOrderModel;
use App\Kuafu\WaiMai\Model\WaiMaiOrderDetailModel;
use App\Kuafu\WaiMai\Model\WaiMaiShopMappingModel;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class DyWaiMaiOrderService extends BaseWaiMaiOrderService
{
    private ?array           $messageArr;
    private int              $orderId;
    private WaiMaiOrderModel $waiMaiOrder;
    private string           $thirdShopId;
    private int              $shopId;
    private int              $type = 0;
    private int              $orderStatus;
    private int              $hand;
    private ?array           $content;

    /**
     * @param int $hand 0自动回调模式 1手动模式
     * @return $this
     */
    public function init(int $hand = 1): self
    {
        $this->hand = $hand;

        if ($hand == 1) {
            $this->messageArr = $this->requestData;
        } else {
            $this->messageArr = json_decode($this->requestData['message'] ?? '', true);
            $this->type       = $this->requestData['type'] ?? 0;
        }
        $contentJson   = $this->messageArr['content'] ?? "";//消息内容，根据需要解析消息内容，不同类型的消息内容不同
        $this->content = $contentJson && is_array($contentJson) ? $contentJson : json_decode($contentJson, true);
        $event         = $this->messageArr['event'] ?? "";//消息类型，用于区分各类消息
        switch ($event) {
            case DyWaiMaiEnum::PAY_SUCCESS_ORDER:
            case DyWaiMaiEnum::ORDER_NOTICE:
                $this->orderId     = (string)($this->content['order']['order_id'] ?? '');
                $this->thirdShopId = (string)($this->content['poi']['poi_id'] ?? '');
                break;
            case DyWaiMaiEnum::REFUND_ORDER_NOTICE:
                $this->orderId     = (string)($this->messageArr['order_id'] ?? "");
                $this->thirdShopId = (string)($this->messageArr['poi_id'] ?? "");
                break;
            case DyWaiMaiEnum::ORDER_FINISH:
                $this->orderId     = (string)($this->content['OrderId'] ?? "");
                $this->thirdShopId = (string)($this->content['PoiId'] ?? "");
                break;
            case DyWaiMaiEnum::ORDER_BILL_CREATE:
                $this->orderId     = (string)($this->content['order']['order_id'] ?? "");
                $this->thirdShopId = (string)($this->content['poi_id'] ?? "");
                break;
            default:
                $this->orderId     = (string)($this->content['order_id'] ?? '');
                $this->thirdShopId = (string)($this->content['poi_id'] ?? '');
        }


        // 转换shopId
        $this->shopId = WaiMaiShopMappingModel::getShopIdByThirdShopId($this->thirdShopId, WaiMaiDBEnum::SOURCE_TYPE_DOUYIN_WM);

        $this->waiMaiOrder = new WaiMaiOrderModel(['orderId' => $this->orderId, 'shopId' => $this->shopId]);

        $this->orderStatus = (int)WaiMaiOrderModel::suffix($this->shopId)
                                                  ->where('orderId', $this->orderId)
                                                  ->value('orderStatus') ?? 0;
        return $this;
    }


    /**
     * 创建抖音订单 - 用户下单并支付成功后，平台会将下单消息推送给第三方系统，消息体中包含订单详细信息，第三方系统可据此消息同步生成订单；
     * @return bool
     * @throws WaiMaiException
     * @throws GuzzleException
     */
    public function createOrder(): bool
    {
        // 针对每个订单加锁
        $this->lockFuncBefore($this->orderId, __FUNCTION__);

        // 转换shopId
        $data['shopId'] = $this->shopId;

        // 判断订单是否存在
        $order = WaiMaiOrderModel::suffix($this->shopId)->where('orderId', $this->orderId)->first();
        if ($order) {

            // 没接单的话，要接单
            if ($order->orderStatus == WaiMaiDBEnum::ORDER_STATUS_1) {

                // 自动接单
                (new DouyinWaiMaiService)->confirmOrder($this->orderId);
            }

            // 存在的直接忽略，会反复推订单过来
            BaseWaiMaiOrderService::$errMsg = '订单已存在';
            $this->lockFuncAfter($this->orderId, __FUNCTION__);
            return true;
        }

        $data['brandId'] = ShopService::getShopInfo($data['shopId'])->brandId ?? 1;


        $data['orderId']    = $this->orderId;
        $orderCreatedAt     = $data['createdAt'] = $data['updatedAt'] = $this->content['order']['create_time'] ?? time();//下单时间戳，单位秒
        $data['sourceType'] = $this->sourceType;
        $data['serialNum']  = $this->content['order']['shop_number'] ?? 0;//流水号

        // 抖音的 - 订单原始金额解读
        //视角1
        //origin_amount=用户实付金额pay_amount+订单折扣金额discount_amount
        //视角2
        //origin_amount=订单内商品售卖金额sale_price+配送费原价
        //配送费原价指的是未计入任何配送费优惠金额的配送费，即
        //1. 自配送模式下，商家设置的用户需要支付的配送费，消息体中会下发
        //2. 平台配送模式下，平台计算出的用户需要支付的配送费，消息体中不会下发

        $data['originalPrice']         = $this->content['amount_info']['origin_amount'] ?? 0;//订单内商品售卖金额sale_price+配送费原价
        $data['payPrice']              = $this->content['amount_info']['pay_amount'] ?? 0;//实付金额，单位分
        $platformDeliverFreightFee     = $this->content['amount_info']['platform_deliver_freight_fee'] ?? 0;//平台配送服务费，单位分（仅平台配送）
        $salePrice                     = $this->content['amount_info']['sale_price'] ?? 0;//订单内商品售卖金额，单位分
        $data['shippingPrice']         = $data['originalPrice'] - $salePrice; // 配送费，单位分
        $data['originalShippingPrice'] = $data['shippingPrice']; // 原价配送费，单位分
        $data['packingPrice']          = 0;//包装费
        $data['orderStatus']           = $orderStatus = WaiMaiDBEnum::ORDER_STATUS_1;//订单状态
        $data['completeTime']          = 0;//完成时间
        $data['acceptTime']            = 0;//接单时间
        $data['remark']                = $this->content['order']['remark'] ?? '';//备注
        $data['platformPrice']         = $this->content['amount_info']['commission_amount'] ?? 0;//平台服务费-佣金，单位分（平台、达人、服务商等各项佣金的总和）
        $data['expectIncome']          = $this->content['amount_info']['estimated_order_income'] ?? 0;//预计收入
        // 统一公式（覆盖所有配送模式）预计收入=商品售卖金额（收入）+ 用户需要支付的配送费（收入，自配） - 商家活动补贴（支出） - 佣金（支出） - 平台配送服务费（支出）-自配送服务费（支出）
        $isBook             = $this->content['order']['is_book'] ?? 0;//是否预订单，1表示即时单、2表示预订单
        $data['expectFast'] = $isBook == 1 ? 1 : 0;//是否期望立即送达 0否 1是
        // 预计送达时间/用户期望送达时间(根据是否预订单)，格式：起始时间戳（单位秒）- 截止时间戳（单位秒），示例：1669370006-1669371206
        list($expectStartTime, $expectEndTime) = explode('-', $this->content['order']['sys_expect_time'] ?? '');
        $data['expectTime']            = $expectStartTime ?? 0; // 期望送达时间
        $data['senderName']            = '';//配送员姓名
        $data['senderPhone']           = '';//配送员电话
        $data['receiverName']          = $this->content['receiver_info']['receiver_name'] ?? '';//收货人姓名
        $data['receiverPhone']         = $this->content['receiver_info']['receiver_phone'] ?? '';//收货人真实电话
        $data['receiverPrivacyPhones'] = $this->content['receiver_info']['secret_number'] ?? '';//收货人隐私号
        $data['receiverAddress']       = ($this->content['receiver_info']['province'] ?? '')
            . ($this->content['receiver_info']['city'] ?? '')
            . ($this->content['receiver_info']['district'] ?? '')
            . ($this->content['receiver_info']['town'] ?? '')
            . ($this->content['receiver_info']['location_name'] ?? '')
            . ($this->content['receiver_info']['door_plate_num'] ?? '');//收货地址 抖音用户侧显示的地址,可参考使用：city+district+town+location_name+door_plate_num
        $data['discountPrice']         = $this->content['amount_info']['discount_amount'] ?? 0;//折扣金额
        $data['redPrice']              = $data['discountPrice']; // 所有优惠金额 折扣金额，单位分（总优惠金额，包含配送费优惠）
        $data['redLists']              = json_encode($this->content['amount_info']['discounts'],
            JSON_UNESCAPED_UNICODE);//活动列表json存 折扣列表
        $data['fullReductionPrice']    = 0;//满减金额
        $data['activityExpenditure']   = $this->content['amount_info']['activities_fee_amount'] ?? 0;//商家活动支出-商家活动补贴，单位分（商品、配送费等各项补贴的总和）
        $merchantDeliverFreightFee     = $this->content['amount_info']['merchant_deliver_freight_fee'] ?? 0;//自配送服务费，单位分（仅自配送）

        // 其他支出 = 菜品原价合计 + 包装费 - 营业收入 - 平台服务费 - 商家活动支出
        $data['otherPrice'] = $salePrice + $data['packingPrice'] - $data['expectIncome'] - $data['platformPrice']
            - $data['activityExpenditure'];  // 其他支出 - 商家app统计用的
        $data['taxpayers']             = "[]";   // 开票信息

        // 把菜篮子的菜品合并起来
        $detailArr = $this->content['products'] ?? [];

        DB::connection('mysqlWaiMai')->beginTransaction();

        $this->waiMaiOrder->addData($data);

        // orderId -> shopId 映射表
        WaiMaiOrderIdShopIdMappingModel::addData($this->orderId, $this->shopId, $this->sourceType);

        $orderDetailData = [];

        foreach ($detailArr as $detail) {

            $productProperty = '';
            $commoditiesArr  = [];// 增加套餐里子菜信息
            $commodities     = $detail['commodities'] ?? [];//菜品搭配
            if ($commodities) {
                foreach ($commodities as $v) {
                    if (isset($v['items']) && $v['items']) {
                        $commoditiesArr = array_merge($commoditiesArr, $v['items']);
                    }
                }
            }

            $orderDetailData[] = [
                'brandId'           => ShopService::getShopInfo($this->shopId)->brandId ?? 0,
                'shopId'            => $this->shopId,
                'orderId'           => $this->orderId,
                'productName'       => $productName = $detail['product_name'] ?? '',//菜品名称
                'productNameStandard' => preg_replace('/\[[^\]]*\]$/', '', $productName),
                'productProperty'   => $productProperty,//菜品属性
                'unit'              => '份',
                'originalUnitPrice' => $originUnitPrice = $detail['origin_amount'] ?? 0,//原单价 商品售卖价
                'num'               => $num = $detail['num'] ?? 0,//菜品数量
                'originalPrice'     => $originUnitPrice * $num,//原总价
                'createdAt'         => $orderCreatedAt,
                'updatedAt'         => $orderCreatedAt,
                'productType'       => WaiMaiOrderDetailModel::PRODUCT_TYPE_1,
                'sourceType'        => $this->sourceType,
                'pocketId'          => $detail['pocketId'] ?? 1,//口袋
                'uniqueId'          => $detail['uniqueId'] ?? '',
                'skuId'             => $detail['sku_id'] ?? '',//skuId
                'commodities'       => $commoditiesArr ? json_encode($commoditiesArr, JSON_UNESCAPED_UNICODE) : null,//菜品详情 json数据
            ];
        }

        // 菜品明细加上包装物
        $this->pushPacking($orderDetailData, $data['packingPrice']);

        // 平台菜的优惠
        $this->shareDetailDiscount($orderDetailData, $data['discountPrice']);

        // 平摊所有菜品的收入
        $this->shareDetailIncomes($orderDetailData, $data['expectIncome']);

        WaiMaiOrderDetailModel::addData($orderDetailData);

        DB::connection('mysqlWaiMai')->commit();

        if (!config('app.pressure')) {

            // 自动接单
            (new DouyinWaiMaiService)->confirmOrder($this->orderId);
        }

        // 自动出餐相关操作
        $this->autoFinish($data);

        $this->lockFuncAfter($this->orderId, __FUNCTION__);

        return true;
    }

    /**
     * 商户接单 - 订单接单后，平台会将订单已接单消息推送给第三方系统
     * @return bool
     * @throws GuzzleException
     * @throws WaiMaiException
     */
    public function acceptOrder(): bool
    {
        $updateTime = time();
        $db         = WaiMaiOrderModel::suffix($this->shopId)->where('orderId', $this->orderId)->count();
        if (!$db) {
            $repair = $this->repairOrder($this->shopId, $this->orderId);
            if (!$repair) {
                return false;
            }
        }

        $this->waiMaiOrder->acceptOrder($updateTime);

        // MQTT
        WaiMaiMqttService::getIns($this->shopId)
                         ->takeOutOrderOperate($this->sourceType, $this->orderId,
                             MqttCodeEnum::TAKE_OUT_OPERATE_TYPE_2);
        return true;
    }

    /**
     * 订单已拒单消息 - 订单被拒单后，平台会将订单已拒单消息推送给第三方系统
     * @return bool
     * @throws GuzzleException
     * @throws WaiMaiException
     */
    public function refuseOrder(): bool
    {
        $updateTime = time();
        $db         = WaiMaiOrderModel::suffix($this->shopId)->where('orderId', $this->orderId)->count();
        if (!$db) {
            $repair = $this->repairOrder($this->shopId, $this->orderId);
            if (!$repair) {
                return false;
            }
        }

        $this->waiMaiOrder->refuseOrder($updateTime);

        // MQTT
        WaiMaiMqttService::getIns($this->shopId)
                         ->takeOutOrderOperate($this->sourceType, $this->orderId,
                             MqttCodeEnum::TAKE_OUT_OPERATE_TYPE_11);
        return true;
    }


    /**
     * 订单已取消消息 - 订单退款成功后，平台会将订单已取消的消息推送给第三方系统，消息中会有具体的取消类型和原因
     * @return bool
     */
    public function cancelOrder(): bool
    {

        $afterSaleId  = $this->messageArr['after_sale_id'] ?? '';//退款id
        $apply_source = $this->messageArr['apply_source'] ?? '';//申请来源

        $reasons = '';
        collect($this->content['reasons'] ?? [])->map(function ($item) use (&$reasons) {
            if (isset($item['msg']) && $item['msg']) {
                $reasons .= $item['msg'];
            }
        });

        $this->waiMaiOrder->applyCancelOrder(WaiMaiDBEnum::ORDER_STATUS_7, $reasons, '');
        // 全额
        $this->waiMaiOrder->cancelOrderFinish();

        // MQTT
        (new MqttService(0, $this->shopId))
            ->takeOutOrder($this->orderId, WaiMaiDBEnum::SOURCE_TYPE_DOUYIN_WM, MqttCodeEnum::TAKE_OUT_OPERATE_TYPE_5, [
                'needConfirm' => false,
            ]);

        $this->finishHook();

        return true;
    }


    /**
     * 订单已同意/已拒绝退款消息 - 1. 用户申请退款后，若退款申请已被处理，平台会将订单已同意/拒绝退款的消息推送给第三方系统，如果是拒绝退款，则消息中会有具体的拒绝原因
     * @return bool
     */
    public function auditRefundOrder(): bool
    {

        $afterSaleId = $this->content['after_sale_id'] ?? '';//退款id
        $pass        = $this->content['pass'] ?? false;//是否同意退款
        $reasons     = '';
        collect($this->content['refuse_audit_reasons'] ?? [])->map(function ($item) use (&$reasons) {
            if (isset($item['msg']) && $item['msg']) {
                $reasons .= $item['msg'];
            }
        });

        if ($pass) {//同意退款
            $this->waiMaiOrder->applyCancelOrder(WaiMaiDBEnum::ORDER_STATUS_7, $reasons, '');
            // 全额
            $this->waiMaiOrder->cancelOrderFinish();

            // MQTT
            (new MqttService(0, $this->shopId))
                ->takeOutOrder($this->orderId, WaiMaiDBEnum::SOURCE_TYPE_DOUYIN_WM,
                    MqttCodeEnum::TAKE_OUT_OPERATE_TYPE_5, [
                        'needConfirm' => false,
                    ]);

            $this->finishHook();
        }

        return true;
    }


    /**
     * 配送状态变更消息 1. 抖音侧配送状态变更，平台会同步状态变更消息给第三方系统
     * @return bool
     */
    public function deliveringOrder(): bool
    {
        $deliveryStatus = $this->content['delivery_status'] ?? 0;//配送状态
        $riderName      = $this->content['rider_name'] ?? '';//骑手姓名
        $riderPhone     = $this->content['rider_phone'] ?? '';//骑手电话
        $updateTime     = $this->content['update_time'] ?? time();//时间 秒时间戳
        $fromThird      = $this->content['from_third'] ?? false;//是否来自服务商回传配送状态 true=是

        // 已完成、取消
        if (in_array($this->orderStatus, WaiMaiDBEnum::ORDER_STATUS_OVER)) {
            BaseWaiMaiOrderService::$errMsg = '订单状态已完结';
            return true;
        }

        // 待配送
        if ($deliveryStatus == DyWaiMaiEnum::LOGISTICS_STATUS_102 || $deliveryStatus == DyWaiMaiEnum::LOGISTICS_STATUS_103) {//骑手已接单 骑手已到店
            $this->waiMaiOrder->waitDeliver($riderName, $riderPhone);

            // MQTT
            (new MqttService(0, $this->shopId))->takeOutOrder($this->orderId, WaiMaiDBEnum::SOURCE_TYPE_ELM,
                MqttCodeEnum::TAKE_OUT_OPERATE_TYPE_6, []);
        }

        // 配送中
        if ($deliveryStatus == DyWaiMaiEnum::LOGISTICS_STATUS_104) {//骑手配送中

            $this->waiMaiOrder->deliveringOrder($riderName, $riderPhone);

            // MQTT
            (new MqttService(0, $this->shopId))->takeOutOrder($this->orderId, WaiMaiDBEnum::SOURCE_TYPE_DOUYIN_WM,
                MqttCodeEnum::TAKE_OUT_OPERATE_TYPE_3, []);
        }

        // 骑手送达 - 订单置为完成
        if ($deliveryStatus == DyWaiMaiEnum::LOGISTICS_STATUS_200) {
            $this->requestData['utime'] = $updateTime;
            $this->completeOrder($updateTime);
        }

        return true;
    }

    // 订单完成
    public function completeOrder($updateTime): bool
    {
        $db = WaiMaiOrderModel::suffix($this->shopId)->where('orderId', $this->orderId)->count();
        if (!$db) {

            $repair = $this->repairOrder($this->shopId, $this->orderId);
            if (!$repair) {
                return false;
            }
        }

        $completeTime = $updateTime;
        $this->waiMaiOrder->completeOrder($completeTime);

        $this->finishHook();

        // MQTT
        (new MqttService(0, $this->shopId))->takeOutOrder($this->orderId, WaiMaiDBEnum::SOURCE_TYPE_DOUYIN_WM,
            MqttCodeEnum::TAKE_OUT_OPERATE_TYPE_4, []);


        return true;
    }


    /**
     * 用户发起取消
     * 1. 当用户发起退款申请，且需要商家审核时，需要通过此接口发消息给第三方系统。
     * 2. 收到用户申请退款消息后，可立即返回处理结果：同意、拒绝、处理中；也可先返回“处理中”，然后再异步处理。建议先返回“处理中”，待商家处理后再异步通过「商家同意/拒绝退款接口」同步处理结果。
     * @return bool
     */
    public function applyCancelOrder(): bool
    {
        // 当前的订单状态
        $orderStatus = BaseWaiMai::getIns($this->sourceType, $this->shopId)
                                 ->getOrderDetail($this->orderId)['data']['orderStatus'] ?? WaiMaiDBEnum::ORDER_STATUS_5;
        if (!in_array($orderStatus, [
            WaiMaiDBEnum::ORDER_STATUS_4,
            WaiMaiDBEnum::ORDER_STATUS_5,
            WaiMaiDBEnum::ORDER_STATUS_6,
            WaiMaiDBEnum::ORDER_STATUS_7
        ])) {// 不能取消

        }
        return true;
    }


    // TODO
    public function shopStatus(): bool
    {
        return true;
    }

    public function shopUnbind(): bool
    {
        return true;
    }

    // 结束触发的钩子
    public function finishHook(): void
    {
        $order = WaiMaiOrderModel::suffix($this->shopId)->where('orderId', $this->orderId)->first();
        if (!$order) {
            return;
        }

        try {
            if ($order->orderStatus != WaiMaiDBEnum::ORDER_STATUS_7) {

                dispatch(function () {

                    Log::info("dy finishHook in...{$this->orderId}");

                    $data = [];

                    $financeOrder         = (new DouyinWaiMaiService)->settleLedgerQueryByOrder((string)$this->orderId);
                    $data['expectIncome'] = $financeOrder['order_amounts'][$this->orderId]['merchant_income'] ?? 0;// 商家实收

                    if ($data['expectIncome'] > 0) {
                        WaiMaiOrderModel::suffix($this->shopId)->where('orderId', $this->orderId)->update($data);

//                        $this->waiMaiOrder->saveBrokerage();// 分账佣金计算
                    } else {
                        Log::info("dy finishHook expectIncome error", [
                            'orderId' => $this->orderId,
                            'shopId'  => $this->shopId,
                            'data'    => $data,
                        ]);
                    }
                })->delay(300)->onQueue('{RMS-API-WM-QUEUE}');

                // 判断是否前天以前的订单
                if ($order->completeTime > 0 && strtotime(date('Y-m-d')) - $order->completeTime > 86400) {
                    // 数据同步更新操作
                    $nsqService = new NsqService();
                    $nsqService->setTopic(NsqCodeEnum::ORDER_NSQ_UPDATE_SEND_QUEUE);
                    $nsqService->setBody([
                        'shopId'    => (string)$this->shopId,
                        'orderId'   => (string)$this->orderId,
                        'orderType' => CodeEnum::SOURCE_TYPE_WAIMAI,
                        'updatedAt' => (string)$order->completeTime
                    ]);
                    $nsqService->sendNsq();
                }
            }

        } catch (\Exception $e) {
            Log::warning($e);
        }
    }

}
